package pt.inescid.components.policyengine.heimdall.commons;

import java.io.FileWriter;
import java.io.IOException;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.Random;
import java.util.Vector;

import pt.inescid.components.policyengine.heimdall.base.EventRelevance;
import pt.inescid.components.policyengine.heimdall.pap.base.CesDefinition;

public class Utilities {

	public static final long THREAD_SLEEP_TIME = 500;

	// Policy 2006
	public static boolean ACTIVATE_INIT = false;
	public static boolean ACTIVATE_CACHE = false;
	public static boolean ACTIVATE_PURGE = true;
	
	// Grid 2006
//	public static int SIMULATION_TYPE = 2;
	// LOCAL = 1
	// DISTRIBUTED = 2
	// REFRESH_DOMAIN = 3
	// PRIVACY = 4

	public static final long SIMULATION_TIME = 5; // IN SECONDS
	public static final int NUMBER_OF_PAYMENT_BATCHS = 4000;
	public static final int PAYMENT_BATCH = 250;
	public static final int TOTAL_EVENTS = NUMBER_OF_PAYMENT_BATCHS * PAYMENT_BATCH;
	public static final int PURGE_THRESHOLD = 5000;
	
	public static final String SPL_POLICIES_DIRECTORY = 
		"G:/docs/Projectos/heimdall/src/policies/";
	public static final String ROOT_POLICY_PATH = 
		"G:/docs/Projectos/heimdall/src";
	public static final String SIMULATIONS_PATH = 
		"G:/docs/Projectos/heimdall/simuls/grid2006/";
	
	public static final int NOT_RELEVANT = 1;
	public static final int RELEVANT_STORE = 2;
	public static final int RELEVANT_BEGINS = 3;
	public static final int RELEVANT_EVOLVES = 4;
	public static final int RELEVANT_FINISHES = 5;

	public static final int NUMBER_OF_EVENTS = 50000; 
	public static final int NUMBER_OF_POLICIES = 1; 
	public static final int NUMBER_OF_AUX_POLICIES = 3; 

	public static final int TRIGGER_POLICY = 0; 
	public static final int EVOLVE_POLICY = 1; 
	public static final int FINISH_POLICY = 2; 
	
	public static final long MINUTES_TO_MILLIS = 60 * 1000;
	public static final long SECONDS_TO_MILLIS = 1000;
    public static final long KILOBYTES_TO_BYTES = 1024;
    public static final long MEGABYTES_TO_BYTES = 1024 * 1024;

    public static final int CHECKPOINT_LEVELS = 5;
	private static Runtime rt = Runtime.getRuntime();
	private static long[] timeOnFirstCheckpoint = new long[Utilities.CHECKPOINT_LEVELS];
	private static long[] timeOnLastCheckpoint = new long[Utilities.CHECKPOINT_LEVELS];
	private static long[] memOnFirstCheckpoint = new long[Utilities.CHECKPOINT_LEVELS];
	private static long[] memOnLastCheckpoint = new long[Utilities.CHECKPOINT_LEVELS];
	private static int[] numberOfPartsOnFirstCheckpoint = new int[Utilities.CHECKPOINT_LEVELS];
	private static int[] numberOfPartsOnLastCheckpoint = new int[Utilities.CHECKPOINT_LEVELS];
	private static String[] computationName = new String[Utilities.CHECKPOINT_LEVELS];
	private static String[] partName = new String[Utilities.CHECKPOINT_LEVELS];

	private static long clockStartTime;
	private static long clockEstimatedFinishTime;
	private static long clockRealFinishTime;
	
	
	public static final int UNDEFINED_INT_VALUE = -1;
	public static final long UNDEFINED_LONG_VALUE = -1;

	
	
	private static FileWriter simulFile = null;

	
	private static Random randomGenerator = null;
	private static boolean randomInitialized = false;
	private static final int MODEL_TAG_SIZE = 3;
	

	
	
/* ===========================================================
==============================================================
=============                    =============================
=============      CONSTRUCTORS  =============================
=============                    =============================
==============================================================
=========================================================== */

/* ===========================================================
==============================================================
=============                    =============================
=============      GETTERS       =============================
=============                    =============================
==============================================================
=========================================================== */


/* ===========================================================
==============================================================
=============                    =============================
=============      SETTERS       =============================
=============                    =============================
==============================================================
=========================================================== */

	public static void createCheckpoint() {
		createCheckpoint("simulationsOutput.txt");
	}

	
	public static void createCheckpoint(String filename) {
		try {
			simulFile = new FileWriter(
					Utilities.SIMULATIONS_PATH + filename);

			simulFile.write(
					"\nN.Parts,PerSecond"
					);
		} catch (IOException e) {
			e.printStackTrace();
			System.exit(-1);
		}
	}
			
	public static void initCheckpoint(int level, String computationName, String partName,
						int totalNumberOfParts) {

		Utilities.computationName[level] = computationName;
		Utilities.partName[level] = partName;
		Utilities.numberOfPartsOnFirstCheckpoint[level] = Utilities.numberOfPartsOnLastCheckpoint[level] = totalNumberOfParts;
		Utilities.timeOnFirstCheckpoint[level] = Utilities.timeOnLastCheckpoint[level] = new java.util.Date().getTime();
		Utilities.memOnFirstCheckpoint[level] = Utilities.memOnLastCheckpoint[level] = rt.totalMemory() - rt.freeMemory();

//		Log.debug(Log.CHECKPOINT,
//				"\n================================================" +
//				"\nCHECKPOINT   ---   CHECKPOINT   ---   CHECKPOINT" +
//				"\n================================================" +
//				"\n" + Utilities.computationName[level] + " - TOTAL: " +
//				Utilities.numberOfPartsOnFirstCheckpoint[level] +
//				" " + Utilities.partName[level] +  " - Level " + level +
//				"\n================================================" +
//				"\nInitialized." +
//				"\nInitial JVM Memory: " + Utilities.getNumberOfBytesString(Utilities.memOnFirstCheckpoint[level]) +
//				"\nMax JVM Memory: " + 0/* not supported in jdk1.3.1 - rt.maxMemory()*/ +
//				"\n================================================");

//		try {
//			simulFile.write(
//					"\n\n\nNEW BATCH\n"
//					);
//		} catch (IOException e) {
//			// TODO Auto-generated catch block
//			e.printStackTrace();
//		}
		
	};	// initCheckpoint



	public static void markCheckpoint(int level, int remainingNumberOfParts) {

		int difNumberOfParts = 0;
		long difTime = 0; // seconds
		long difMem = 0;
		int difNumberOfPartsFromFirstCheckpoint = 0;
		long difTimeFromFirstCheckpoint = 0;
		long difMemFromFirstCheckpoint = 0;
		long estimatedTimeToCompletion = 0; // seconds
		long currentTime = new java.util.Date().getTime();
		long currentMem = rt.totalMemory() - rt.freeMemory();
		float millisPerPart;
		float partialMillisPerPart;
		
		difNumberOfParts = Utilities.numberOfPartsOnLastCheckpoint[level] - remainingNumberOfParts;
		difTime = (currentTime - Utilities.timeOnLastCheckpoint[level]) / Utilities.SECONDS_TO_MILLIS;
		difMem = currentMem - Utilities.memOnLastCheckpoint[level];
		difNumberOfPartsFromFirstCheckpoint = Utilities.numberOfPartsOnFirstCheckpoint[level] - remainingNumberOfParts;
		difTimeFromFirstCheckpoint = (currentTime - Utilities.timeOnFirstCheckpoint[level]) / Utilities.SECONDS_TO_MILLIS;
		difMemFromFirstCheckpoint = currentMem - Utilities.memOnFirstCheckpoint[level];

		if (difTimeFromFirstCheckpoint != 0) {
			estimatedTimeToCompletion = (long) ((float) difNumberOfPartsFromFirstCheckpoint /
					difTimeFromFirstCheckpoint * remainingNumberOfParts);
		}
		else {
			estimatedTimeToCompletion = 0;
		}

		Utilities.timeOnLastCheckpoint[level] = currentTime;
		Utilities.numberOfPartsOnLastCheckpoint[level] = remainingNumberOfParts;

		long partsPerSecond;
		if (difTimeFromFirstCheckpoint != 0) {
			partsPerSecond = difNumberOfPartsFromFirstCheckpoint/difTimeFromFirstCheckpoint;
		}
		else {
			partsPerSecond = 0;
		}
		millisPerPart = ((float) difTimeFromFirstCheckpoint) /
						((float) difNumberOfPartsFromFirstCheckpoint);
		partialMillisPerPart = ((float) difTime) /
		((float) difNumberOfParts);
		
		long partialPartsPerSecond;
		if (difTime != 0) {
			partialPartsPerSecond = difNumberOfParts / difTime;
		}
		else {
			partialPartsPerSecond = 0;
		}
		
		Log.debug(Log.CHECKPOINT,
				"\n" +
				"\n============================================================" +
				"\nCHECKPOINT   ---   CHECKPOINT   ---   CHECKPOINT" +
				"\n============================================================" +
				"\n" + Utilities.computationName[level] + " - TOTAL: " +
				Utilities.numberOfPartsOnFirstCheckpoint[level] +
				" " + Utilities.partName[level] + " - Level " + level +
				"\nInicial JVM memory: " + Utilities.getNumberOfBytesString(Utilities.memOnFirstCheckpoint[level]) +
				"\n============================================================" +
				"\nCompleted " + difNumberOfParts + " " +
				Utilities.partName[level] + " in " + difTime + " seconds with " +
				Utilities.getNumberOfBytesString(difMem) + "bytes (Sec/Part: " +
				partialMillisPerPart + ")." +
				"\nRemaining " + remainingNumberOfParts + " " +
				Utilities.partName[level] +
				"\nTime to Date: " + difTimeFromFirstCheckpoint + " seconds." +
				"\nParts/Sec: " + partsPerSecond +
				"\nMem to Date: " + Utilities.getNumberOfBytesString(difMemFromFirstCheckpoint) + " bytes." +
				"\nTotal JVM Memory: " + Utilities.getNumberOfBytesString(currentMem) + " bytes." +
				"\nETC: " + estimatedTimeToCompletion +
				" seconds = " + (estimatedTimeToCompletion / 60) + " minutes." +
				"\n============================================================" +
				"\n",
				"Utilities.markCheckpoint");

		try {
			simulFile.write(
					"\n" + (difNumberOfPartsFromFirstCheckpoint-1) + "," +
					partialMillisPerPart
					);
		} catch (IOException e) {
			e.printStackTrace();
		}
		
		
	};	// markCheckpoint


	public static void startClock(long secondsToFinish) {
		clockStartTime = System.currentTimeMillis();
		clockEstimatedFinishTime = clockStartTime + 
				secondsToFinish * SECONDS_TO_MILLIS;
	};

	
	public static boolean checkClock(long partsFinished) {
		clockRealFinishTime = System.currentTimeMillis();

		if (clockRealFinishTime > clockEstimatedFinishTime) {
	
			try {
				simulFile.write(
						"\n" + partsFinished);
			} catch (IOException e) {
				e.printStackTrace();
			}
		
			return (true);
		}
		return (false);
	}
			
	
	
	public static void flush() {
		try {
			simulFile.flush();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	
	
	public static String getNumberOfBytesString(long mem) {
		return ("" + (mem / Utilities.MEGABYTES_TO_BYTES) +
				"," + ((mem % Utilities.MEGABYTES_TO_BYTES) / Utilities.KILOBYTES_TO_BYTES) +
				"," + (mem % Utilities.KILOBYTES_TO_BYTES));
	}

	public static String capitalize(String currentString) {
		return (currentString.substring(0,1) + currentString.substring(1));
	}

	
//	public static long getCurrentTime() {
//		GregorianCalendar now = new GregorianCalendar();
//		
//		return (now.getTime().getTime());
//	} // getCurrentTime
	

	//  ----------------------------------------------------------
//  ------------------------   joinVectors   ------------------------
//  ----------------------------------------------------------
	public static Vector joinVectors(Vector a, Vector b) {
		if (a == null) {
			a = new Vector();
		}
		
		if (b != null) {
			for (int i = 0; i < b.size(); i++) {
				a.add(b.elementAt(i));
			}
		}
		
		return (a);
	}

	//  ----------------------------------------------------------
//  ------------------------   joinParametersNoDuplicates   ------------------------
//  ----------------------------------------------------------
	public static Vector joinParametersNoDuplicates(Vector a, Vector b) {
		if (a == null) {
			return (null);
		}
		
		if (b != null) {
			EventRelevance parameter1 = null;
			EventRelevance parameter2 = null;
			boolean found = false;
			for (int i = 0; i < b.size(); i++) {
				parameter1 = (EventRelevance) b.elementAt(i);
				
				found = false;
				for (int j = 0; j < a.size(); j++) {
					parameter2 = (EventRelevance) a.elementAt(j);
					
					if (parameter1.getFieldName() ==
						parameter2.getFieldName()) {
						found = true;
						j = a.size();
					}
				}
				
				if (!found) {
					a.add(parameter1);
				}
			}
		}
		
		return (a);
	}

	
	//  ----------------------------------------------------------
//  ------------------------   joinCesNoDuplicates   ------------------------
//  ----------------------------------------------------------
	public static Vector joinCesNoDuplicates(Vector a, Vector b) {
		if (a == null) {
			return (null);
		}
		
		if (b != null) {
			CesDefinition parameter1 = null;
			CesDefinition parameter2 = null;
			boolean found = false;
			for (int i = 0; i < b.size(); i++) {
				parameter1 = (CesDefinition) b.elementAt(i);
				
				found = false;
				for (int j = 0; j < a.size(); j++) {
					parameter2 = (CesDefinition) a.elementAt(j);
					
					if (parameter1.getName().equals(parameter2.getName())) {
						found = true;
						j = a.size();
					}
				}
				
				if (!found) {
					a.add(parameter1);
				}
			}
		}
		
		return (a);
	}

	

	
	//  ----------------------------------------------------------
//  ------------------------   copyVector   ------------------------
//  ----------------------------------------------------------
	public static Vector copyVector(Vector original) {
		if (original == null) {
			return (null);
		}
		
		Vector result = new Vector();
		for (int i = 0; i < original.size(); i++) {
			result.add(original.elementAt(i));
		}
		
		return (result);
	}
	
		//  ----------------------------------------------------------
//  ------------------------   removeDuplicates   ------------------------
//  ----------------------------------------------------------
	public static Vector removeDuplicates(Vector original) {
		if (original == null) {
			return (null);
		}
		
		Vector reduced = (Vector) original.clone();
		Object currentRecord = null;
		Object nextRecord = null;

		for (int i = 0; i < reduced.size(); i++) {
			currentRecord = reduced.elementAt(i);
			for (int j = i+1; j < reduced.size(); j++) {
				nextRecord = reduced.elementAt(j);
				
				if (currentRecord.equals(nextRecord)) {
					reduced.removeElementAt(j);
					j--;
				}
			}
		}
		
		return (reduced);
	} // removeDuplicateRelevantFields


	//  ----------------------------------------------------------
//  ------------------------   substKeywords   ------------------------
//  ----------------------------------------------------------
	public static String substKeywords(String original) {
		if (original == null) {
			return (null);
		}

		String result = original;

		result = result.replace("@CURRENT_TIME", new Long(TimeUtilities.getCurrentTime()).toString());
		
		if (result.contains("DAY") ||
				result.contains("HOUR") ||
				result.contains("MINUTE") ||
				result.contains("SECOND")) {
			result = new Long(TimeUtilities.parseTimeQty(result)).toString();
		}
		
		return (result);
	}
	
	
	/* ===========================================================
	 ==============================================================
	 =============                    =============================
	 =============      getFormattedNumber =========================
	 ==========================================
	 ==============================================================
	 =========================================================== */
	public static String getFormattedNumber(int anInteger, int totalDigits) {
		
		String integerAsString = String.valueOf(anInteger);
		String zeros = "";
		for (int i = 0; i < totalDigits; i++) {
			zeros += "0";
		}
		
		integerAsString = zeros.substring(0, zeros.length()-
				integerAsString.length()) + integerAsString;
		
		return (integerAsString);
	} // getFormattedNumber

	
	public static void main(String[] args) {
		String a = getFormattedNumber(1,4);
		a = null;
	}
	
	
	
	/* ===========================================================
	 ==============================================================
	 =============                    =============================
	 =============      getFormattedTimeString =========================
	 ==========================================
	 ==============================================================
	 =========================================================== */
	public static String getFormattedTimeString(long time) {
		GregorianCalendar auxCalendar;
		String day, month, year;

		if (time <= 0) {
			return("");
		}
		else {
			auxCalendar = new GregorianCalendar();
			auxCalendar.setTime(new java.util.Date(time));

			day = new String("" + auxCalendar.get(Calendar.DAY_OF_MONTH));
			if (day.length() == 1) {
				day = new String("0") + day;
			};
			month = new String("" + (auxCalendar.get(Calendar.MONTH) + 1));
			if (month.length() == 1) {
				month = new String("0") + month;
			};
			year = new String("" + auxCalendar.get(Calendar.YEAR));

			return (year + "-" + month + "-" + day);
		}
	};

	/* ===========================================================
	 ==============================================================
	 =============                    =============================
	 =============      getFormattedHourString =========================
	 ==========================================
	 ==============================================================
	 =========================================================== */
	public static String getFormattedHourString(long time) {
		GregorianCalendar auxCalendar;
		String hour, minute, second, millis;

		if (time <= 0) {
			return("-");
		}
		else {
			auxCalendar = new GregorianCalendar();
			auxCalendar.setTime(new java.util.Date(time));

			hour = new String("" + auxCalendar.get(Calendar.HOUR_OF_DAY));

			minute = new String("" + auxCalendar.get(Calendar.MINUTE));
			if (minute.length() == 1) {
				minute = new String("0") + minute;
			};
			
			second = new String("" + auxCalendar.get(Calendar.SECOND));
			if (second.length() == 1) {
				second = new String("0") + second;
			};

			millis = new String("" + auxCalendar.get(Calendar.MILLISECOND));

			return (hour + ":" + minute + ":"  + second+ "." + millis);
		}
	};

	/* ===========================================================
	 ==============================================================
	 =============                    =============================
	 =============      getCompleteFormattedDateString =========================
	 ==========================================
	 ==============================================================
	 =========================================================== */
	public static String getCompleteFormattedDateString(long time) {
		GregorianCalendar auxCalendar;
		String hours, minutes, seconds, day, month, year;

		if (time <= 0) {
			return("-");
		}
		else {
			auxCalendar = new GregorianCalendar();
			auxCalendar.setTime(new java.util.Date(time));

			hours = new String("" + auxCalendar.get(Calendar.HOUR_OF_DAY));
			minutes = new String("" + auxCalendar.get(Calendar.MINUTE));
			if (minutes.length() == 1) {
				minutes = new String("0") + minutes;
			};
			seconds = new String("" + auxCalendar.get(Calendar.SECOND));
			if (seconds.length() == 1) {
				seconds = new String("0") + seconds;
			};
			day = new String("" + auxCalendar.get(Calendar.DAY_OF_MONTH));
			if (day.length() == 1) {
				day = new String("0") + day;
			};
			month = new String("" + (auxCalendar.get(Calendar.MONTH) + 1));
			if (month.length() == 1) {
				month = new String("0") + month;
			};
			year = new String("" + auxCalendar.get(Calendar.YEAR));

			return (year + "-" + month + "-" + day + " " + 
					hours + ":" + minutes + ":" + seconds);
		}
	};

	
	/* ===========================================================
	==============================================================
	==============================================================
	getNewInstanceTag
	==============================================================
	==============================================================
	=========================================================== */
	public static String getNewInstanceTag() {
		long seed = new Date().getTime();

		if (!randomInitialized) {
			randomGenerator = new Random(seed);
			randomInitialized = true;
		}
		
		byte token[] = new byte[MODEL_TAG_SIZE];
		randomGenerator.nextBytes(token);
		
		final int characterRange = 'Z' - 'A' + 1;
		for (int i = 0; i < MODEL_TAG_SIZE; i++) {
			token[i] %= characterRange;
			if (token[i] < 0) {
				token[i] = (byte) -token[i];
			}
			token[i] += 'A';
		}
		
		return new String(token);
	} // getNewInstanceTag

	

}; // Utilities


